/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
module dwt.program.program;


private import dwt.dwt;

private import dwt.internal.win32.os;
private import dwt.internal.compatibility;
private import dwt.graphics.image;
private import dwt.graphics.imagedata;
private import dwt.util.javatypes;
private import dwt.util.util;
 

/**
 * Instances of this class represent programs and
 * their assoicated file extensions in the operating
 * system.
 */
public class Program {
	
	char[] name;
	char[] command;
	char[] iconName;

/**
 * Prevents uninitialized instances from being created outside the package.
 */
this () {
}

/**
 * Finds the program that is associated with an extension.
 * The extension may or may not begin with a '.'.  Note that
 * a <code>Display</code> must already exist to guarantee that
 * this method returns an appropriate result.
 *
 * @param extension the program extension
 * @return the program or <code>null</code>
 *
 * @exception SWTError <ul>
 *		<li>ERROR_NULL_ARGUMENT when extension is null</li>
 *	</ul>
 */
public static Program findProgram (char[] extension) {
	if (extension is null) {
		DWT.error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
		return null;
	}
	if (extension.length == 0) return null;
	
	if (extension[0] != '.') 
		extension = "." ~ extension; //$NON-NLS-1$
	HKEY phkResult;
	if (OS.RegOpenKeyEx (OS.HKEY_CLASSES_ROOT, Converter.StrToTCHARz(extension), 0, OS.KEY_READ, &phkResult) != 0) {
		return null;
	}	
	uint lpcbData = 256;
	TCHAR[] lpData = new TCHAR[lpcbData];
	lpcbData = lpcbData*TCHAR.sizeof;
	int result = OS.RegQueryValueEx (phkResult, null, null, null, cast(ubyte*)lpData, &lpcbData);
	OS.RegCloseKey (phkResult);
	if (result != 0) return null;
	return getProgram (Converter.TCHARzToStr(lpData.ptr));
}

/**
 * Answer all program extensions in the operating system.  Note
 * that a <code>Display</code> must already exist to guarantee
 * that this method returns an appropriate result.
 *
 * @return an array of extensions
 */
public static char[][] getExtensions () {
	char[][] extensions;
	TCHAR[] lpName = new TCHAR[1024];
	uint lpcName = lpName.length;
	FILETIME ft;
	int dwIndex = 0;
	while (OS.RegEnumKeyEx (OS.HKEY_CLASSES_ROOT, dwIndex, lpName.ptr, &lpcName, null, null, null, &ft) != OS.ERROR_NO_MORE_ITEMS) {
		char[] extension = Converter.TCHARzToStr(lpName.ptr, lpcName);
		lpcName = lpName.length;
		if (extension.length > 0 && extension[0] == '.') {
			extensions ~= extension;
		}
		dwIndex++;
	}

	return extensions;
}

static char[] getKeyValue (char[] string, boolean expand) {
	/* Use the character encoding for the default locale */
	HKEY phkResult;
	if (OS.RegOpenKeyEx (OS.HKEY_CLASSES_ROOT, Converter.StrToTCHARz(string), 0, OS.KEY_READ, &phkResult) != 0) {
		return null;
	}
	char[] result = "";
	uint lpcbData;
	if (OS.RegQueryValueEx (phkResult,  null, null, null, null, &lpcbData) == 0) {
		int _length = lpcbData / TCHAR.sizeof;
		if (_length != 0) {
			TCHAR[] lpData = new TCHAR[_length];
			if (OS.RegQueryValueEx (phkResult, null, null, null, cast(BYTE*)lpData, &lpcbData) == 0) {
				if (!OS.IsWinCE && expand) {
					_length = OS.ExpandEnvironmentStrings (lpData.ptr, null, 0);
					if (_length != 0) {
						TCHAR[] lpDst = new TCHAR[_length];
						OS.ExpandEnvironmentStrings (lpData.ptr, lpDst.ptr, _length);
						result = Converter.TCHARzToStr(lpDst.ptr);
					}
				} else {
					_length = Math.max (0, cast(int)lpData.length - 1);
					result = Converter.TCHARzToStr(lpData.ptr, _length);
				}
			}
		}
	}
	if (phkResult !is null) OS.RegCloseKey (phkResult);
	return result;
}

static Program getProgram (char[] key) {

	/* Name */
	char[] name = getKeyValue (key, false);
	if (name is null || name.length == 0) return null;

	/* Command */
	char[] DEFAULT_COMMAND = ("\\shell"); //$NON-NLS-1$
	char[] defaultCommand = getKeyValue (key ~ DEFAULT_COMMAND, true);
	if (defaultCommand is null) 
		defaultCommand = ("open"); //$NON-NLS-1$
	char[] COMMAND = ("\\shell\\") ~ defaultCommand ~ "\\command"; //$NON-NLS-1$
	char[] command = getKeyValue (key ~ COMMAND, true);
	if (command is null || command.length == 0) return null;

	/* Icon */
	char[] DEFAULT_ICON = ("\\DefaultIcon"); //$NON-NLS-1$
	char[] iconName = getKeyValue (key ~ DEFAULT_ICON, true);
	if (iconName is null || iconName.length == 0) return null;

	Program program = new Program ();
	program.name = name;
	program.command = command;
	program.iconName = iconName;
	return program;
}

/**
 * Answers all available programs in the operating system.  Note
 * that a <code>Display</code> must already exist to guarantee
 * that this method returns an appropriate result.
 *
 * @return an array of programs
 */
public static Program [] getPrograms () {
	Program [] programs;
	/* Use the character encoding for the default locale */
	TCHAR[] lpName = new TCHAR[1024];
	uint lpcName = lpName.length;
	FILETIME ft;
	int dwIndex = 0;
	while (OS.RegEnumKeyEx (OS.HKEY_CLASSES_ROOT, dwIndex, lpName.ptr, &lpcName, null, null, null, &ft) != OS.ERROR_NO_MORE_ITEMS) {	
		char[] path = Converter.TCHARzToStr(lpName.ptr, lpcName);
		lpcName = lpName.length;
		Program program = getProgram (path);
		if (program !is null) {
			programs ~= program;
		}
		dwIndex++;
	}

	return programs;
}

/**
 * Launches the executable associated with the file in
 * the operating system.  If the file is an executable,
 * then the executable is launched.  Note that a <code>Display</code>
 * must already exist to guarantee that this method returns
 * an appropriate result.
 *
 * @param fileName the file or program name
 * @return <code>true</code> if the file is launched, otherwise <code>false</code>
 * 
 * @exception SWTError <ul>
 *		<li>ERROR_NULL_ARGUMENT when fileName is null</li>
 *	</ul>
 */
public static boolean launch (char[] fileName) {
	if (fileName is null) {
		DWT.error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
		return false;
	}
	
	SHELLEXECUTEINFO info;
	info.cbSize = SHELLEXECUTEINFO.sizeof;
	info.lpFile = Converter.StrToTCHARz(fileName);
	info.nShow = OS.SW_SHOW;
	
	boolean result = OS.ShellExecuteEx (&info);
		
	return result;
}

/**
 * Executes the program with the file as the single argument
 * in the operating system.  It is the responsibility of the
 * programmer to ensure that the file contains valid data for 
 * this program.
 *
 * @param fileName the file or program name
 * @return <code>true</code> if the file is launched, otherwise <code>false</code>
 * 
 * @exception SWTError <ul>
 *		<li>ERROR_NULL_ARGUMENT when fileName is null</li>
 *	</ul>
 */
public boolean execute (char[] fileName) {
	if (fileName is null) {
		DWT.error(__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
		return false;
	}
	boolean quote = true;
	char[] prefix = command, suffix = ""; //$NON-NLS-1$
	int index = Converter.find(command, "%1"); //$NON-NLS-1$
	if (index != -1) {
		int count=0;
		int i=index + 2, len = command.length;
		while (i < len) {
			if (command[i] == '"') count++;
			i++;
		}
		quote = count % 2 == 0;
		prefix = Converter.substring(command, 0,index);
		suffix = Converter.substring(command, index + 2, len);
	}
	if (quote) fileName = " \"" ~ fileName ~ "\""; //$NON-NLS-1$ //$NON-NLS-2$
	try {
		Compatibility.exec(prefix ~ fileName ~ suffix);
	} catch (Exception e) {
		return false;
	}
	return true;
}

/**
 * Returns the receiver's image data.  This is the icon
 * that is associated with the reciever in the operating
 * system.
 *
 * @return the image data for the program, may be null
 */
public ImageData getImageData () {
	int nIconIndex = 0;
	char[] fileName = iconName;
	int index = Converter.indexOf(iconName, ',');
	if (index != -1) {
		fileName = Converter.substring(iconName, 0, index);
		char[] iconIndex = Converter.strip( Converter.substring(iconName, index + 1) );
		try {
			nIconIndex = Integer.parseInt (iconIndex);
		} catch (Exception e) {}
	}
	HICON phiconSmall, phiconLarge;
	OS.ExtractIconEx (Converter.StrToTCHARz(fileName), nIconIndex, &phiconLarge, &phiconSmall, 1);
	if (phiconSmall is null) return null;
	Image image = Image.win32_new (null, DWT.ICON, phiconSmall);
	ImageData imageData = image.getImageData ();
	image.dispose ();
	return imageData;
}

/**
 * Returns the receiver's name.  This is as short and
 * descriptive a name as possible for the program.  If
 * the program has no descriptive name, this string may
 * be the executable name, path or empty.
 *
 * @return an the name of the program
 */
public char[] getName () {
	return name;
}

/**
 * Returns true if the receiver and the argument represent
 * the same program.
 * 
 * @return true if the programs are the same
 */
public boolean equals(Object other) {
	if (this is other) return true;
	if (cast(Program)other) {
		Program program = cast(Program)other;
		return name == (program.name) && command == (program.command)
			&& iconName == (program.iconName);
	}
	return false;
}

/**
 * Returns a hash code suitable for this object.
 * 
 * @return a hash code
 */
public int hashCode() {
	assert(false);
	//return strcrc32(name) ^ strcrc32(command) ^ strcrc32(iconName);
	return -1;
}

public char[] toString() {
	
	return "Program {" ~ name ~ "}"; //$NON-NLS-1$ //$NON-NLS-2$
}

}
